/* ******************************* *
 *       -=# Array Life #=-        *
 *             =====               *
 *         By: TarpeyD12           *
 * =============================== *
 *        Version: 0.3.5.9         *
 * ******************************* *
 *     Internal Function file.     *
 *        alfunc_internal.c        *
 * ******************************* */

#include "array_life.h"

int gridsize(int gridsize_x, int gridsize_y, int dna_size);
int move_cell(int cdnts_x, int cdnts_y, int dir, int dna_size);
int copy(int cdnts_x, int cdnts_y, int dir, int dna_size);
int create(int cc_x, int cc_y, int dna_size);
int kill_(int kc_x, int kc_y, int dna_size);
int mutate(int mutate_x, int mutate_y, int dna_size);
int decode_dir_x(int s_x, int dir);
int decode_dir_y(int s_y, int dir);
int strength(int aa_x, int aa_y, int dna_size);
int chk_cell(int chk_x, int chk_y);
int chk_grid();
int cell_cnt(int gs_x, int gs_y, int dna_size);
int chk_dna();
int strongest();
int weakest();
int make_wall(int type, int startx, int starty, int endx, int endy);

/* the arrays and grids. */
extern int grid_box[GRIDSIZEX][GRIDSIZEY][DNASIZE];
extern int occp[GRIDSIZEX][GRIDSIZEY];

/* what loop it is. */
extern int loop;
	
int gridsize(int gridsize_x, int gridsize_y, int dna_size) { /* creates: main grid, dna array, and occupation cells. */
	int gm_x=0, gm_y=0, gm_dna=0;
	for(gm_x=0;gm_x<gridsize_x;gm_x++) {
		for(gm_y=0;gm_y<gridsize_y;gm_y++) {
			for(gm_dna=0;gm_dna<dna_size;gm_dna++) {
				grid_box[gm_x][gm_y][gm_dna]=0; /* creates the main grid values plus the dna sequence array values. */
			}
			occp[gm_x][gm_y]=0; /* creates the occupation grid values. */
		}
	}
	/* cleans up. */
	free(malloc(gm_x));
	free(malloc(gm_y));
	free(malloc(gm_dna));
	free(malloc(gridsize_x));
	free(malloc(gridsize_y));
	free(malloc(dna_size));
	
	return 1;
};

int move_cell(int cdnts_x, int cdnts_y, int dir, int dna_size) { /* moves the cell */
	
	int dna_tmp=0;
	int dirtmp_x=0, dirtmp_y=0;

	if(occp[cdnts_x][cdnts_y]==1) { /* checks if originating cell is occupied. */
	   dirtmp_x=cdnts_x;
	   dirtmp_y=cdnts_y;
		switch(dir) { /* processes the direction. */
			case 1: /* SW */dirtmp_x--; dirtmp_y++; break;
			case 2: /* S  *//*dirtmp_x;*/dirtmp_y++;break;
			case 3: /* SE */dirtmp_x++; dirtmp_y++; break;
			case 4: /* W  */dirtmp_x--;/*dirtmp_y;*/break;
			case 5: /* stationary. */default: return 6;
			case 6: /* E  */dirtmp_x++;/*dirtmp_y;*/break;
			case 7: /* NW */dirtmp_x--; dirtmp_y--; break;
			case 8: /* N  *//*dirtmp_x;*/dirtmp_y--;break;
			case 9: /* NE */dirtmp_x++; dirtmp_y--; break;
		}
		if(dirtmp_x<=0){return 11;}
		if(dirtmp_y<=0){return 11;}
		if(dirtmp_x>=(GRIDSIZEX-1)){return 11;}
		if(dirtmp_y>=(GRIDSIZEY-1)){return 11;}
		if(occp[decode_dir_x(cdnts_x, dir)][decode_dir_y(cdnts_y, dir)]==WALL_CELL) {return 11;}
		if(occp[decode_dir_x(cdnts_x, dir)][decode_dir_y(cdnts_y, dir)]==TEMP_CELL) {return 11;}
		if(occp[dirtmp_x][dirtmp_y]==0) { /* checks if destination cell is occupied. */
			occp[cdnts_x][cdnts_y]=0; /* kills startcell. */
			occp[dirtmp_x][dirtmp_y]=3; /* creates the new cell occupation cordanates. */
			for(dna_tmp=0;dna_tmp<dna_size;dna_tmp++) {
			/*wprintw(out_scr, "------%d------\n", dna_tmp);*/
				if(grid_box[cdnts_x][cdnts_y][dna_tmp]!='\0'){
					switch(grid_box[cdnts_x][cdnts_y][dna_tmp]) {
						case 1:	grid_box[dirtmp_x][dirtmp_y][dna_tmp]=1;	break;
						case 0:	grid_box[dirtmp_x][dirtmp_y][dna_tmp]=0;	break;
					}
				grid_box[cdnts_x][cdnts_y][dna_tmp]=0; /* erases start cell dna. */
				}
			}
			return (dir+1);
		}
		else
		{
			/*wprintw(out_scr, "ERR: move cell from grid_box[%d][%d] to grid_box[%d][%d], destination cell is occupied: %d\n",cdnts_x, cdnts_y, dirtmp_x, dirtmp_y, dir);*/
			return 11;
		}
		
	}else{ wprintw(out_scr, "ERR: move cell from grid_box[%d][%d]: no cell present.\n", cdnts_x, cdnts_y); return 11;}
	return 11;
};

int copy(int cdnts_x, int cdnts_y, int dir, int dna_size) { /* copys the cell */
	
	int dna_tmp=0;
	int dirtmp_x=0, dirtmp_y=0;
	
	if(occp[cdnts_x][cdnts_y]==1) { /* checks if originating cell is occupied. */
	   dirtmp_x=cdnts_x;
	   dirtmp_y=cdnts_y;
		switch(dir) { /* processes the direction. */
			case 1: /* SW */dirtmp_x--;dirtmp_y++;break;
			case 2: /* S  *//*dirtmp_x;*/  dirtmp_y++;break;
			case 3: /* SE */dirtmp_x++;dirtmp_y++;break;
			case 4: /* W  */dirtmp_x--;/*dirtmp_y;*/  break;
			case 5: /* stationary. */default: return 6;
			case 6: /* E  */dirtmp_x++;/*dirtmp_y;*/  break;
			case 7: /* NW */dirtmp_x--;dirtmp_y--;break;
			case 8: /* N  *//*dirtmp_x;*/  dirtmp_y--;break;
			case 9: /* NE */dirtmp_x++;dirtmp_y--;break;
		}
		if(dirtmp_x<=0){return 11;}
		if(dirtmp_y<=0){return 11;}
		if(dirtmp_x>=(GRIDSIZEX-1)){return 11;}
		if(dirtmp_y>=(GRIDSIZEY-1)){return 11;}
		if(occp[decode_dir_x(cdnts_x, dir)][decode_dir_y(cdnts_y, dir)]==WALL_CELL) {return 11;}
		if(occp[decode_dir_x(cdnts_x, dir)][decode_dir_y(cdnts_y, dir)]==TEMP_CELL) {return 11;}
		if(occp[dirtmp_x][dirtmp_y]==0) { /* checks if destination cell is occupied. */
			occp[dirtmp_x][dirtmp_y]=3; /* creates the new cell on the occupation cordanates. */
			for(dna_tmp=0;dna_tmp<dna_size;dna_tmp++) {
				/*wprintw(out_scr, "------%d------\n", dna_tmp);*/
				if(grid_box[cdnts_x][cdnts_y][dna_tmp]!='\0'){
					switch(grid_box[cdnts_x][cdnts_y][dna_tmp]) {
						case 1: grid_box[dirtmp_x][dirtmp_y][dna_tmp]=1; break;
						case 0: grid_box[dirtmp_x][dirtmp_y][dna_tmp]=0; break;
					}
				}
			}
			return (dir+1);
		}
		else
		{
		   /*wprintw(out_scr, "ERR: copy cell from grid_box[%d][%d] to grid_box[%d][%d], destination cell is occupied: %d\n",cdnts_x, cdnts_y, dirtmp_x, dirtmp_y, dir);*/
		   return 11;
		}
		
	}else{ wprintw(out_scr, "ERR: copy cell from grid_box[%d][%d]: no cell present.\n", cdnts_x, cdnts_y); return 11;}
	return 11;
};

int create(int cc_x, int cc_y, int dna_size) { /* creates a cell with ran dna sequence in spescified grid_box. */
	int long rndm=0;
	int loop=0;
	if(occp[cc_x][cc_y]==0) {
		occp[cc_x][cc_y]=1;
		for(loop=0;dna_size>loop;loop++) {
			rndm = RDM() % 2;
			/*wprintw(out_scr, "===%d===", rndm);*/
			grid_box[cc_x][cc_y][loop]=rndm;
		}
	}else{ wprintw(out_scr, "ERR: create_cell at grid_box[%d][%d]: cell already exists.\n", cc_x, cc_y); }
	/* cleans up. */
	free(malloc(cc_x));
	free(malloc(cc_y));
	free(malloc(dna_size));
	free(malloc(rndm));
	free(malloc(loop));
	return 1;
};

int kill_(int k_x, int k_y, int dna_size){
	int dna_tmp=0;
	if(occp[k_x][k_y]==DEAD_CELL){
		wprintw(out_scr, "ERR: kill cell: grid[%d][%d] is unnocccupied.\n", k_x, k_y);
	}
	else{ 
		/*cell(k_x, k_y)=DEAD_CELL;*/ /* when uncommented causes screen to be blank(but not in debug mode!?) */
		for(dna_tmp=0;dna_tmp<dna_size;dna_tmp++){grid_box[k_x][k_y][dna_tmp]=0;}
	}
	return 1;
};

int mutate(int mutate_x, int mutate_y, int dna_size) { /* mutates the 'dna' of the spescified occupied cell. */
	/*int dna_tmp =0;*/
	int rndm = 0;
	if(occp[mutate_x][mutate_y]==1 || occp[mutate_x][mutate_y]==3) {  /* sees if spescified cell is occupied. */
		rndm = RDM();
		rndm %= dna_size;
		/*wprintw(out_scr, "\n***%d***\n", rndm+1);*/
		switch(grid_box[mutate_x][mutate_y][rndm]) {
			case 0: grid_box[mutate_x][mutate_y][rndm]=1; break;
			case 1: grid_box[mutate_x][mutate_y][rndm]=0; break;
		}
		return 1/*(rndm+2)*/;
	}else{ wprintw(out_scr, "ERR: mutate at cell[%d][%d]: dosent exist.\n", mutate_x, mutate_y); return 1; }
};

int decode_dir_x(int s_x, int dir){	
	switch(dir-1) { /* processes the direction. */
		case 0: return (s_x);
		case 1: /* SW */return(s_x-1);
		case 2: /* S  */return(s_x);
		case 3: /* SE */return(s_x+1);
		case 4: /* W  */return(s_x-1);
		case 5: /* stationary. */return(s_x);
		case 6: /* E  */return(s_x+1);
		case 7: /* NW */return(s_x-1);
		case 8: /* N  */return(s_x);
		case 9: /* NE */return(s_x+1);
		case 10: return(s_x);
		default: wprintw(out_scr, "ERR: decode_dir_x: not a valid direction.\n"); return(s_x);
	}
};

int decode_dir_y(int s_y, int dir){
	switch(dir-1) { /* processes the direction. */
		case 0: return (s_y);
		case 1: /* SW */return(s_y+1);
		case 2: /* S  */return(s_y+1);
		case 3: /* SE */return(s_y+1);
		case 4: /* W  */return(s_y);
		case 5: /* stationary. */return(s_y);
		case 6: /* E  */return(s_y);
		case 7: /* NW */return(s_y-1);
		case 8: /* N  */return(s_y-1);
		case 9: /* NE */return(s_y-1);
		case 10: return(s_y);
		default: wprintw(out_scr, "ERR: decode_dir_y: not a valid direction.\n"); return(s_y);
	}
};

int strength(int aa_x, int aa_y, int dna_size){ /* returns the sum of the amino acids of given cell. */
	int dna_tmp=0, str=0, alp=0, i;
	char aastmp[(dna_size/3)][3];
	
	char aas1[]="010100110";	int aas_1=10;	/* 21 */
	char aas2[]="101011";   	int aas_2=10;	/* 19 */
	char aas3[]="000101";   	int aas_3=10;	/* 15 */
	char aas4[]="101001010";	int aas_4=10;	/* 18 */
	char aas5[]="110100";   	int aas_5=10;	/* 19 */
	char aas6[]="101110010";	int aas_6=10;	/* 23 */
	char aas7[]="011000100";	int aas_7=10;	/* 17 */
	char aas8[]="100001";   	int aas_8=10;	/* 14 */
	char aas9[]="001011";   	int aas_9=10;	/* 15 */
	char aas0[]="111111";   	int aas_0=-20;	/* -6 */
	
	for(dna_tmp=0, alp=0;dna_tmp<dna_size;dna_tmp++, alp++){
		if(grid_box[aa_x][aa_y][dna_tmp]==0 && grid_box[aa_x][aa_y][(dna_tmp+1)]==0 && grid_box[aa_x][aa_y][(dna_tmp+2)]==0)
			{str+=0; aastmp[alp][0]='0';aastmp[alp][1]='0';aastmp[alp][2]='0';}/* 000 */
		else if(grid_box[aa_x][aa_y][dna_tmp]==0 && grid_box[aa_x][aa_y][(dna_tmp+1)]==0 && grid_box[aa_x][aa_y][(dna_tmp+2)]==1)
			{str+=1; aastmp[alp][0]='0';aastmp[alp][1]='0';aastmp[alp][2]='1';}/* 001 */
		else if(grid_box[aa_x][aa_y][dna_tmp]==0 && grid_box[aa_x][aa_y][(dna_tmp+1)]==1 && grid_box[aa_x][aa_y][(dna_tmp+2)]==0)
			{str+=2; aastmp[alp][0]='0';aastmp[alp][1]='1';aastmp[alp][2]='0';}/* 010 */
		else if(grid_box[aa_x][aa_y][dna_tmp]==1 && grid_box[aa_x][aa_y][(dna_tmp+1)]==0 && grid_box[aa_x][aa_y][(dna_tmp+2)]==0)
			{str+=3; aastmp[alp][0]='1';aastmp[alp][1]='0';aastmp[alp][2]='0';}/* 100 */
		else if(grid_box[aa_x][aa_y][dna_tmp]==0 && grid_box[aa_x][aa_y][(dna_tmp+1)]==1 && grid_box[aa_x][aa_y][(dna_tmp+2)]==1)
			{str+=4; aastmp[alp][0]='0';aastmp[alp][1]='1';aastmp[alp][2]='1';}/* 011 */
		else if(grid_box[aa_x][aa_y][dna_tmp]==1 && grid_box[aa_x][aa_y][(dna_tmp+1)]==0 && grid_box[aa_x][aa_y][(dna_tmp+2)]==1)
			{str+=5; aastmp[alp][0]='1';aastmp[alp][1]='0';aastmp[alp][2]='1';}/* 101 */
		else if(grid_box[aa_x][aa_y][dna_tmp]==1 && grid_box[aa_x][aa_y][(dna_tmp+1)]==1 && grid_box[aa_x][aa_y][(dna_tmp+2)]==0)
			{str+=6; aastmp[alp][0]='1';aastmp[alp][1]='1';aastmp[alp][2]='0';}/* 110 */
		else if(grid_box[aa_x][aa_y][dna_tmp]==1 && grid_box[aa_x][aa_y][(dna_tmp+1)]==1 && grid_box[aa_x][aa_y][(dna_tmp+2)]==1)
			{str+=7; aastmp[alp][0]='1';aastmp[alp][1]='1';aastmp[alp][2]='1';}/* 111 */
		dna_tmp++;
		dna_tmp++;
	}
	for(i=0;i<(dna_size/3);i++) {
		if(strncmp(aastmp[i], aas1, strlen(aas1))==0){str+=aas_1;}
		else if(strncmp(aastmp[i], aas2, strlen(aas2))==0){str+=aas_2;}
		else if(strncmp(aastmp[i], aas3, strlen(aas3))==0){str+=aas_3;}
		else if(strncmp(aastmp[i], aas4, strlen(aas4))==0){str+=aas_4;}
		else if(strncmp(aastmp[i], aas5, strlen(aas5))==0){str+=aas_5;}
		else if(strncmp(aastmp[i], aas6, strlen(aas6))==0){str+=aas_6;}
		else if(strncmp(aastmp[i], aas7, strlen(aas7))==0){str+=aas_7;}
		else if(strncmp(aastmp[i], aas8, strlen(aas8))==0){str+=aas_8;}
		else if(strncmp(aastmp[i], aas9, strlen(aas9))==0){str+=aas_9;}
		else if(strncmp(aastmp[i], aas0, strlen(aas0))==0){str+=aas_0;}
	}
	/* cleans up. */
	free(malloc(aa_x));
	free(malloc(aa_y));
	free(malloc(dna_size));
	free(malloc(dna_tmp));
	free(malloc(alp));
	free(malloc(i));
	
	free((char*)calloc(sizeof(aas1), sizeof(char)));
	free((char*)calloc(sizeof(aas2), sizeof(char)));
	free((char*)calloc(sizeof(aas3), sizeof(char)));
	free((char*)calloc(sizeof(aas4), sizeof(char)));
	free((char*)calloc(sizeof(aas5), sizeof(char)));
	free((char*)calloc(sizeof(aas6), sizeof(char)));
	free((char*)calloc(sizeof(aas7), sizeof(char)));
	free((char*)calloc(sizeof(aas8), sizeof(char)));
	free((char*)calloc(sizeof(aas9), sizeof(char)));
	free((char*)calloc(sizeof(aas0), sizeof(char)));
	
	free((int*)calloc(sizeof(aastmp), sizeof(int)));
	
	free(malloc(aas_1));
	free(malloc(aas_2));
	free(malloc(aas_3));
	free(malloc(aas_4));
	free(malloc(aas_5));
	free(malloc(aas_6));
	free(malloc(aas_7));
	free(malloc(aas_8));
	free(malloc(aas_9));
	free(malloc(aas_0));
	
	return (str);
};

int chk_cell(int chk_x, int chk_y) { /* checks a cell for invalid occupation values. */
	if(occp[chk_x][chk_y]!=0 && occp[chk_x][chk_y]!=1 && occp[chk_x][chk_y]!=2 && occp[chk_x][chk_y]!=3){
		wprintw(out_scr, "ERR: Ocupation: occp[%d][%d]: %d.\n", chk_x, chk_y, occp[chk_x][chk_y]);
		return (occp[chk_x][chk_y]);
	}
	return 1;
}

int chk_grid() { /* checks the entire grid for cells with invalid occupation values. */
	int chkx, chky;
	for(chkx=0;chkx<=GRIDSIZEX;chkx++){
		for(chky=0;chky<=GRIDSIZEY;chky++){
			if(chk_cell(chkx, chky)!=1){ /*needs chk_cell(); function to opperate properly. */
				wprintw(out_scr, "ERR: cell[%d][%d]=%d.\n", chkx, chky, occp[chkx][chky]);
				kill(chkx, chky, DNASIZE);
			}
		}
	}
	refresh();
	return 1;
};

int cell_cnt(int gs_x, int gs_y, int dna_size) {
	int x, y, cnt=0;
	chk_grid(); /* requires the chk_grid(); to be functioning */
	for(x=0;x<gs_x;x++){
		for(y=0;y<gs_y;y++) {
			if(occp[x][y]==1){
				cnt++;
			}
		}
	}
	/*if(cnt==0){wprintw(out_scr, "\nno living cells.\n");}*/
	refresh();
	return(cnt);
};

int chk_dna() {
	int dna_tmp, dna_cnt;
	int x, y;
	for(x=0;x<GRIDSIZEX;x++) {
		for(y=0;y<GRIDSIZEY;y++) {
			if(occp[x][y]==1) {
				for(dna_tmp=0, dna_cnt=0;dna_tmp<DNASIZE;dna_tmp++) {
					switch(grid_box[x][y][dna_tmp]) {
						case 0:	break;
						case 1:	dna_cnt++; break;
					}
				}
				/* requires kill() function to 'func'. */
				if(dna_cnt==0){
					kill(x, y, DNASIZE);
					wprintw(out_scr, "ERR: corrupted cell at grid_box[%d][%d], killing it.\n", x, y);}
				if(dna_cnt==DNASIZE){
					kill(x, y, DNASIZE);
					wprintw(out_scr, "ERR: corrupted cell at grid_box[%d][%d], killing it.\n", x, y);}
			}
		}
	}
	return 1;	
};

int strongest() { /* returns the value of the strength of the strongest cell. */
	int gest=0, x=0, y=0;

	for(x=0;x<GRIDSIZEX;x++){
		for(y=0;y<GRIDSIZEY;y++) {
			if(occp[x][y]==1){
				if(strength(x, y, DNASIZE)>gest){
					gest=strength(x, y, DNASIZE);
				}
			}
		}
	}
	return (gest);
};

int weakest() { /* returns the value of the strength of the weakest cell. */
	int gest=strongest(), x=0, y=0;

	for(x=0;x<GRIDSIZEX;x++){
		for(y=0;y<GRIDSIZEY;y++) {
			if(occp[x][y]==1){
				if(strength(x, y, DNASIZE)<gest){
					gest=strength(x, y, DNASIZE);
				}
			}
		}
	}
	return (gest);
};

int make_wall(int type, int startx, int starty, int endx, int endy) {
	int x=0, y=0;
	
	switch(type) {
		case SOLID:
			for(x=startx;x<=endx;x++){
				for(y=starty;y<=endy;y++){
					wall(x, y);
				}
			}
		break;
		
		case HOLLOW:
			make_wall(SOLID, startx, starty, startx, endy);
			make_wall(SOLID, startx, starty, endx, starty);
			make_wall(SOLID, startx, endy, endx, endy);
			make_wall(SOLID, endx, starty, endx, endy);
		break;
		
		case LATIS:
			for(x=startx;x<=endx;x++){
				for(y=starty;y<=endy;y++){
					if( x%2==0 ^ y%2==0 ){
						wall(x, y);
					}
				}
			}
		break;
		
		case LATWALL:
			make_wall(LATIS, startx, starty, startx, endy);
			make_wall(LATIS, startx, starty, endx, starty);
			make_wall(LATIS, startx, endy, endx, endy);
			make_wall(LATIS, endx, starty, endx, endy);		
		break;
	}
	return(type);
};

int not_cell(int chk_x, int chk_y) {
	if(occp[chk_x][chk_y]!=0 && occp[chk_x][chk_y]!=1 && occp[chk_x][chk_y]!=2 && occp[chk_x][chk_y]!=3)
	{return TRUE;}
	else{return FALSE;}
};
